#ifndef _NET_TOOL
#define _NET_TOOL

#include <cstdlib>
#include "Exception.h"
#include <mutex>

#ifdef __linux
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define SOCKET_T int
#endif

#ifdef __MINGW32__
#include <winsock2.h>
#include <ws2def.h>
#include<windows.h>
#define SOCKET_T SOCKET
#define SHUT_RDWR SD_BOTH
#endif
namespace nettool {
    /**
     * 向socket描述符发送数据，阻塞尽力发送全部，出错抛出SocketException异常
     * @throw SocketException
     */
    void send(SOCKET_T fd, const char * buf, ssize_t size);
}
typedef struct socketinf {
    SOCKET_T socket_fd;
    sockaddr_in addr;
    
    /**
     * 以线程安全的方式获取目标IP地址
     * @param buf 保存IP地址的buffer 必须确保至少16Byte大小，否则将出现段错误
     * 
     */
    void getip(char * buf);
    void close();
}socketinf;
// inet_ntoa函数全局锁，防止内部静态内存脏读写
extern std::mutex inet_ntoa_mutex;

/**
 *  在一个已绑定端口且监听的服务器socket上接收一个连接
 *  @param serverSocket 服务器socket
 *  @return socketinf
 *  @throw SocketException
 */
socketinf Accept(SOCKET_T serverSocket) ;

/**
 * 服务器监听一个端口
 * @param addr 监听地址
 * @param port 端口号
 * @param socket_fd 将被初始化的socket描述符指针
 * @throw SocketException
 */
bool listen_server(const char * addr, unsigned short port, int * socket_fd) ;

/**
 * 连接到服务器
 * @param addr 目标地址
 * @param port 目标端口
 * @param timeout 超时时间（单位:秒）
 */
socketinf connect_to(const char * addr, unsigned short port,int timeout) ;

/**
 * 在某地址和端口上启动一个服务，并启动一个控制该服务的线程
 * @param addr 监听地址
 * @param port 端口号
 * @param handler_function 控制函数指针，会传递一个存放于堆中的int*型参数作为服务端socket，需要手动free
 * @throw errno
 */
bool create_service(const char * addr, unsigned short port, void *(*handler_function)(void *)) ;

// 判断是否为域名
// 是返回true 否则返回false
bool isDomainName(const char * address);

/**
 * 将域名解析为IP地址
 * @param DN 域名
 * @param ip IP地址结果存放
 * @return 成功true 失败false
 */
bool DNtoIP(const char *DN, char *ip);
unsigned long long ntohll(unsigned long long val);
unsigned long long htonll(unsigned long long val);
#endif